package validation.jexp;

import com.jgoodies.validation.ValidationResult;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EventObject;
import java.util.LinkedList;
import java.util.List;

public class JGoodiesValidationUtils {

    private static ValidationResult errorsResult;
    private static ValidationResult warningsResult;
    private static ValidationResult mixedResult;


    private JGoodiesValidationUtils() {
        // Suppresses default constructor, ensuring non-instantiability.
    }


    // Misc *******************************************************************

    /**
     * Returns a listener that writes bean property changes to the console.
     * The log entry includes the PropertyChangeEvent's source, property name,
     * old value, and new value.
     *
     * @return a debug listener that logs bean changes to the console
     */
    public static PropertyChangeListener createDebugPropertyChangeListener() {
        PropertyChangeListener listener = new DebugPropertyChangeListener();
        debugListeners.add(listener);
        return listener;
    }


    /**
     * Creates and returns an Action that exists the system if performed.
     *
     * @return an Action that exists the system if performed
     *
     * @see System#exit(int)
     */
    public static Action getCloseAction() {
        return new CloseAction();
    }


    // Example Validation Results *********************************************

    /**
     * Lazily creates and returns an example ValidationResult
     * that contains only errors.
     *
     * @return a ValidationResult that contains only errors
     */
    public static ValidationResult getErrorsResult() {
        if (errorsResult == null) {
            errorsResult = new ValidationResult();
            errorsResult.addError("The order no is mandatory.");
            errorsResult.addError("The order date is mandatory.");
        }
        return errorsResult;
    }


    /**
     * Lazily creates and returns an example ValidationResult
     * that contains errors and warnings.
     *
     * @return a ValidationResult that contains errors and warnings
     */
    public static ValidationResult getMixedResult() {
        if (mixedResult == null) {
            mixedResult = new ValidationResult();
            mixedResult.addWarning("The order no length shall be in [5, 10].");
            mixedResult.addError("The order date is mandatory.");
        }
        return mixedResult;
    }


    /**
     * Lazily creates and returns an example ValidationResult
     * that contains only warnings.
     *
     * @return a ValidationResult that contains only warnings
     */
    public static ValidationResult getWarningsResult() {
        if (warningsResult == null) {
            warningsResult = new ValidationResult();
            warningsResult.addWarning("The order no length shall be in [5, 10].");
            warningsResult.addWarning("The order date shall be in the future.");
        }
        return warningsResult;
    }


    // Screen Position ********************************************************

    /**
     * Locates the given component on the screen's center.
     *
     * @param component   the component to be centered
     */
    public static void locateOnScreenCenter(Component component) {
        Dimension paneSize = component.getSize();
        Dimension screenSize = component.getToolkit().getScreenSize();
        component.setLocation(
                (screenSize.width  - paneSize.width)  / 2,
                (screenSize.height - paneSize.height) / 2);
    }


    /**
     * Opens a message dialog that shows the validation result,
     * things the user must fix, before the selection can change.
     *
     * @param headerText        the text displayed above the validation message
     * @param validationResult  the validation result to be displayed
     *
     * @throws IllegalArgumentException if the validation result is empty
     */
    public static void showValidationMessage(
            EventObject e,
            String headerText,
            ValidationResult validationResult) {
        if (validationResult.isEmpty())
            throw new IllegalArgumentException("The validation result must not be empty.");

        Object eventSource = e.getSource();
        Component parent = null;
        if (eventSource instanceof Component) {
            parent = SwingUtilities.windowForComponent((Component) eventSource);
        }
        showValidationMessage(parent, headerText, validationResult);
    }

    public static void showValidationMessage(Component parent, String headerText, ValidationResult validationResult) {

        if (validationResult.isEmpty())
            throw new IllegalArgumentException("The validation result must not be empty.");

        boolean error = validationResult.hasErrors();
        String messageText = headerText + "\n\n" + validationResult.getMessagesText() + "\n\n";
        String titleText   = "Validation " + (error ? "Error" : "Warning");
        int messageType    = error ? JOptionPane.ERROR_MESSAGE : JOptionPane.WARNING_MESSAGE;
        JOptionPane.showMessageDialog(parent, messageText, titleText, messageType);
    }

    // Debug Listener *********************************************************

    /**
     * Used to hold debug listeners, so they won't be removed by
     * the garbage collector, even if registered by a listener list
     * that is based on weak references.
     *
     * @see #createDebugPropertyChangeListener()
     * @see
     */
    private static List debugListeners = new LinkedList();


    /**
     * Writes the source, property name, old/new value to the system console.
     */
    private static class DebugPropertyChangeListener implements PropertyChangeListener {

        public void propertyChange(PropertyChangeEvent evt) {
            System.out.println();
            System.out.println("The source: " + evt.getSource());
            System.out.println(
                    "changed '" + evt.getPropertyName()
                            + "' from '" + evt.getOldValue()
                            + "' to '" + evt.getNewValue() + "'.");
        }
    }


    // Actions ****************************************************************

    /**
     * An Action that exists the System.
     */
    private static final class CloseAction extends AbstractAction {

        private CloseAction() {
            super("Close");
        }

        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    }

    public static void showFrame(String title, JComponent panel) {
        JFrame frame = new JFrame();
        frame.setTitle(title);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        locateOnScreenCenter(frame);
        frame.setVisible(true);
    }
}
